﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Network.Interface.Packets;

namespace Network.Server.ClientHandling
{
    public abstract class ClientsHandler : IDisposable
    {
        protected readonly Thread HandlingThread;
        public abstract int ClientsCount { get; }

        public Action<string> Log { get; set; }

        protected ClientsHandler()
        {
            HandlingThread = new Thread(HandleClients);
            HandlingThread.Start();
        }

        public abstract void HandleClients();

        public abstract void Handle(UdpClient udpServer, IPEndPoint remote, byte[] received);


        public abstract void Stop(bool terminateGentle);

        protected void AddToLog(string text)
        {
            if (Log != null)
                Log(text);
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            HandlingThread.Abort();
        }

        #endregion

    }

    public class ClientsHandler<TClient, TServerPacket, TClientPacket> : ClientsHandler
        where TServerPacket : ServerPacket, new()
        where TClientPacket : ClientPacket, new()
        where TClient : ClientHandler<TServerPacket, TClientPacket>
    {
        private readonly Dictionary<string, TClient> mClients;
        private bool mTerminate;

        public ClientsHandler()
        {
            mTerminate = false;
            mClients = new Dictionary<string, TClient>();
        }

        public override void Handle(UdpClient udpServer, IPEndPoint remote, byte[] received)
        {
            TClient client;
            string ip = remote.ToString();
            if (!mClients.TryGetValue(ip, out client) && !mTerminate)
            {
                AddToLog(string.Format("Client from {0} connected.", remote));
                client = (TClient)Activator.CreateInstance(typeof(TClient), udpServer, remote);
                if (Log != null)
                    client.Log = Log;
                mClients.Add(ip, client);
            }
            if(client != null)
                client.AddTask(received);
        }

        public override int ClientsCount
        {
            get { return mClients.Count; }
        }

        public override void HandleClients()
        {
            while (!mTerminate || mClients.Count > 0)
            {
                mClients.Where(x => !x.Value.IsActive).Select(x => x.Key).ToList().ForEach(x => mClients.Remove(x));
                Thread.Sleep(ServerConfigurator.Instance.HandlerUpdateTime);
            }
        }

        #region Implementation of IDisposable

        public override void Stop(bool terminateGentle)
        {
            mTerminate = true;
            if(!terminateGentle)
                mClients.ToList().ForEach(x => x.Value.DropClient());
            else
                while (mClients.Count > 0)
                    Thread.Sleep(ServerConfigurator.Instance.HandlerUpdateTime);
        }

        #endregion
    }
}
